HTTP over Time Travel is a hintRange: <byte_start>:<byte_end>dd if=flag.txt bs=1 seek=[REDACTED] conv=notrunc of=haystack.binimport requests
current_offset = 0
SESS = requests.Session()
while True:
chunk = SESS.get("https://futuredisk-web.chal.uiuc.tf/haystack.bin.gz", headers={"Range": "bytes={}-{}".format(current_offset, current_offset+2048)}).content
print(chunk)import requests
target_signature = b"\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\x03\xec\xc1\x01\x01\x00\x00\x00\x80\x90\xfe\xaf\xee\x08\n"
current_offset = 0
chunk_size = 2048
while True:
response = requests.get(
"https://futuredisk-web.chal.uiuc.tf/haystack.bin.gz",
headers={"Range": f"bytes={current_offset}-{current_offset + chunk_size - 1}"}
)
chunk = response.content
if b"\x00" not in chunk and target_signature not in chunk:
print("Matched data found in chunk starting at offset", current_offset)
print(chunk)
current_offset += chunk_sizeSorry, we’ve pinged the challenge author - think they are currently asleep. Will let you know when they are awake
It's important to note that since the file is empty, downloading it will not take up excessive bandwidth or storage on the user's side. how tf do they think i should download itb'\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\x03\xec\xc1\x01\x01\x00\x00\x00\x80\x90\xfe\xaf\xee\x08\n\x00...Range: bytes=9223372036854775799-9223372036854775807 to get the last 8 bytesRange: bytes=9223372036854775799-9223372036854775807 to get the last 8 bytes 9223372036854775807 is the whole lengthimport requests
target_signature = b"\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\x03\xec\xc1\x01\x01\x00\x00\x00\x80\x90\xfe\xaf\xee\x08\n"
current_offset = 0
chunk_size = 2048
while True:
response = requests.get(
"https://futuredisk-web.chal.uiuc.tf/haystack.bin.gz",
headers={"Range": f"bytes={current_offset}-{current_offset + chunk_size - 1}"}
)
chunk = response.content
if b"\x00" not in chunk and target_signature not in chunk:
print("Matched data found in chunk starting at offset", current_offset)
print(chunk)
current_offset += chunk_size if'sfallocate -l 10mb example.bindd if=flag.txt bs=1 seek=1024 conv=notrunc of=example.bingzip example.binexample.bin at the top or endhaystack.bin isnt at their file's top lol the provided files are seekable, and
multiple reads from the same offset of the same file are guaranteed to
return identical data.Strellic — Today at 4:13 PM
💀
this seems like range header
this seems like compression forensics
which is fake web
presumably you can read the gzip header or something and maybe it tells you where the flag isimport requests
target_signature = b"\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\x03\xec\xc1\x01\x01\x00\x00\x00\x80\x90\xfe\xaf\xee\x08\n"
current_offset = 0
chunk_size = 2048
while True:
response = requests.get(
"https://futuredisk-web.chal.uiuc.tf/haystack.bin.gz",
headers={"Range": f"bytes={current_offset}-{current_offset + chunk_size - 1}"}
)
chunk = response.content
if b"\x00" not in chunk and target_signature not in chunk:
print("Matched data found in chunk starting at offset", current_offset)
print(chunk)
current_offset += chunk_size b"\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\x03\xec\xc1\x01\x01\x00\x00\x00\x80\x90\xfe\xaf\xee\x08\n" if you query first few bytes you get this 2.3. Member format
Each member has the following structure:
+---+---+---+---+---+---+---+---+---+---+
|ID1|ID2|CM |FLG| MTIME |XFL|OS | (more-->)
+---+---+---+---+---+---+---+---+---+---+ 2.3. Member format
Each member has the following structure:
+---+---+---+---+---+---+---+---+---+---+
|ID1|ID2|CM |FLG| MTIME |XFL|OS | (more-->)
+---+---+---+---+---+---+---+---+---+---+ Digest header alr?etag1f 8b 08 08
they set it as 1f 8b 08 00
The last byte is FLG
This flag byte is divided into individual bits as follows:
bit 0 FTEXT
bit 1 FHCRC
bit 2 FEXTRA
bit 3 FNAME
XFL is set to 02
our example set as 00
U...UUUUUUUUUUUUUUUUUU\xa5=8 \x00\x00\x00\x00\x10\xd2\xff\xd5\x1dA\x00\x00\x00\x00\x00...\x00 are useful i thinkUUU...U with a new line, but it's the same thing over and over?
Idk how this helpsUUU...U with a new line, but it's the same thing over and over?
Idk how this helps '\xc3\x84\xc5\xb8\xc3\x89\x03\x01\x00\x00\x00\x00 \xcb\x87\xe2\x97\x8aFPa\x0f\x0e\x04\x00\x00\x00\x00\xc3\x84\xc2\xb8_\x1bA\xc3\x96=8\x10\x00\x00\x00\x00\x00\xc3\x9a\x7fm\x04\x15\xcb\x86\xe2\x80\xa1@\x00\x00\x00\x00\x00\xc2\xbb\xcb\x87\xc2\xb5\x11T'
'\xc3\xbf\xc3\x89\x03\x01\x00\x00\x00\x00 \xcb\x87\xe2\x97\x8aFPa\x0f\x0e\x04\x00\x00\x00\x00\xc3\x84\xc2\xb8_\x1bA\xc3\x96=8\x10\x00\x00\x00\x00\x00\xc3\x9a\x7fm\x04\x15\xcb\x86\xe2\x80\xa1@\x00\x00\x00\x00\x00\xc2\xbb\xcb\x87\xc2\xb5\x11T'
'\xc3\xbf\xc3\x89\x03\x01\x00\x00\x00\x00 \xcb\x87\xe2\x97\x8aFPa\x0f\x0e\x04\x00\x00\x00\x00\xc3\x84\xc2\xb8_\x1bA\xc3\x96=8\x10\x00\x00\x00\x00\x00\xc3\x9a\x7fm\x04\x15\xcb\x86\xe2\x80\xa1@\x00\x00\x00\x00\x00\xc2\xbb\xcb\x87\xc2\xb5\x11T'
It's the same except for the very beginning
If it's empty, it seems like a simpler repetition, idk1f 8b 08 08
they set it as 1f 8b 08 00
The last byte is FLG
This flag byte is divided into individual bits as follows:
bit 0 FTEXT
bit 1 FHCRC
bit 2 FEXTRA
bit 3 FNAME
b'\x1f\xc3\xa3\x08\x02\x03\xc3\x8f\xc2\xa1\x01\x01\xc3\x84\xc3\xaa\xcb\x9b\xc3\x98\xc3\x93\x08\n\xc3\x84\xc5\xb8\xc3\x89\x03\x01 \xcb\x87\xe2\x97\x8aFPa\x0f\x0e\x04\xc3\x84\xc2\xb8_\x1bA\xc3\x96=8\x10\xc3\x9a\x7fm\x04\x15\xcb\x86\xe2\x80\xa1@\xc2\xbb\xcb\x87\xc2\xb5\x11T\xc3\xbf\xc3\x89\x03\x01 \xcb\x87\xe2\x97\x8aFPa\x0f\x0e\x04\xc3\x84\xc2\xb8_\x1bA\xc3\x96=8\x10\xc3\x9a\x7fm\x04\x15\xcb\x86\xe2\x80\xa1@\xc2\xbb\xcb\x87\xc2\xb5\x11T\xc3\xbf\xc3\x89\x03\x01 \xcb\x87\xe2\x97\x8aFPa\x0f\x0e\x04\xc3\x84\xc2\xb8_\x1bA\xc3\x96=8\x10\xc3\x9a\x7fm\x04\x15\xcb\x86\xe2\x80\xa1@\xc2\xbb\xcb\x87\xc2\xb5\x11T'
the range is bytes=0-100000 and replace "U" and "\x00", but the headers are different?b'\x1f\xc3\xa3\x08\x02\x03\xc3\x8f\xc2\xa1\x01\x01\xc3\x84\xc3\xaa\xcb\x9b\xc3\x98\xc3\x93\x08\n\xc3\x84\xc5\xb8\xc3\x89\x03\x01 \xcb\x87\xe2\x97\x8aFPa\x0f\x0e\x04\xc3\x84\xc2\xb8_\x1bA\xc3\x96=8\x10\xc3\x9a\x7fm\x04\x15\xcb\x86\xe2\x80\xa1@\xc2\xbb\xcb\x87\xc2\xb5\x11T\xc3\xbf\xc3\x89\x03\x01 \xcb\x87\xe2\x97\x8aFPa\x0f\x0e\x04\xc3\x84\xc2\xb8_\x1bA\xc3\x96=8\x10\xc3\x9a\x7fm\x04\x15\xcb\x86\xe2\x80\xa1@\xc2\xbb\xcb\x87\xc2\xb5\x11T\xc3\xbf\xc3\x89\x03\x01 \xcb\x87\xe2\x97\x8aFPa\x0f\x0e\x04\xc3\x84\xc2\xb8_\x1bA\xc3\x96=8\x10\xc3\x9a\x7fm\x04\x15\xcb\x86\xe2\x80\xa1@\xc2\xbb\xcb\x87\xc2\xb5\x11T'
the range is bytes=0-100000 and replace "U" and "\x00", but the headers are different? import requests
chunk = requests.get("https://futuredisk-web.chal.uiuc.tf/haystack.bin.gz", headers={"Range":"bytes=0-100000"}).content
out_file = open("out.bin", "wb")
chunk = chunk.replace(b"UU", b"").replace(b"\x00", b"")
out_file.write(chunk)bytes write gigabytes Uncompressed text, on
the other hand, will probably still be readable despite the presence
of some corrupted bytes. wonder how many bytes we need to get to be able to do thisimport requests
huh = 8211
def pain2(offset, l):
response = requests.get(
"https://futuredisk-web.chal.uiuc.tf/haystack.bin.gz",
headers={"Range": f"bytes={offset}-{offset + l + 1}"}
)
chunk = response.content
return chunk
print(pain2(huh, 32))\x00s to a ton of Us)b'\x00\x00\x00\x80\xd9\x83\x03\x01\x00\x00\x00\x00 \xff\xd7FPUUUUUUUUUUUUUUUUU'00000080d98303010000000020ffd74650555555555555555555555555555555555580d98303010000000020ffd74650\x00 to Uimport requests
huh = 8214
def pain2(offset, l):
response = requests.get(
"https://futuredisk-web.chal.uiuc.tf/haystack.bin.gz",
headers={"Range": f"bytes={offset}-{offset + l - 1}"}
)
chunk = response.content
return chunk
print(pain2(huh, 14)) (edited)import requests
huh = 8214
def pain2(offset, l):
response = requests.get(
"https://futuredisk-web.chal.uiuc.tf/haystack.bin.gz",
headers={"Range": f"bytes={offset}-{offset + l - 1}"}
)
chunk = response.content
return chunk
print(pain2(huh, 14)) (edited)import requests
huh = 8214
def pain2(offset, l):
response = requests.get(
"https://futuredisk-web.chal.uiuc.tf/haystack.bin.gz",
headers={"Range": f"bytes={offset}-{offset + l - 1}"}
)
chunk = response.content
return chunk
print(pain2(huh, 14)) (edited)80d98303010000000020ffd74650 4650 part from?Almost all of the remainder of the file (until the trailer, which begins on byte 4696) is interpreted according to the Huffman tables in figures 8 and 9. As you can see, the Huffman compression tables are very efficient; only 65 bytes of the 4,704-byte gzipped file are "dictionary" information that's needed to interpret the remainder of the file, with an additional 20 bytes of header information and 8 bytes of trailer information. (edited)gunzip -c final.bin [14:53:14]
gzip: final.bin: invalid compressed data--format violatedimport requests
huh = 8214
out = open("final.bin", "wb")
def header(offset, l):
response = requests.get(
"https://futuredisk-web.chal.uiuc.tf/haystack.bin.gz",
headers={"Range": f"bytes={offset}-{offset + l - 1}"}
)
chunk = response.content
return chunk
def bin(offset, l):
response = requests.get(
"https://futuredisk-web.chal.uiuc.tf/haystack.bin.gz",
headers={"Range": f"bytes={offset}-{offset + l - 1}"}
)
chunk = response.content
return chunk
def trailer():
resp = requests.head("https://futuredisk-web.chal.uiuc.tf/haystack.bin.gz")
cl = resp.headers['content-length']
offset = int(cl) - 8
response = requests.get(
"https://futuredisk-web.chal.uiuc.tf/haystack.bin.gz",
headers={"Range": f"bytes={offset}-{cl}"}
)
chunk = response.content
return chunk
head = header(0, 24)
binflag = bin(huh, 14)
trail = trailer()
out.write(head)
out.write(binflag)
out.write(trail)gunzip -c final.bin [14:53:14]
gzip: final.bin: invalid compressed data--format violated b'\x00\x00\x00\x80\xd9\x83\x03\x01\x00\x00\x00\x00 \xff\xd7FPUUUUUUUUUUUUUUUUU' d88303010000000020ffd74650 d88303010000000020ffd74650
80d98303010000000020ffd74650
only different in first 2 bytes (edited)import requests
huh = 8214
out = open("final.bin", "wb")
def header(offset, l):
response = requests.get(
"https://futuredisk-web.chal.uiuc.tf/haystack.bin.gz",
headers={"Range": f"bytes={offset}-{offset + l - 1}"}
)
chunk = response.content
return chunk
def bin(offset, l):
response = requests.get(
"https://futuredisk-web.chal.uiuc.tf/haystack.bin.gz",
headers={"Range": f"bytes={offset}-{offset + l - 1}"}
)
chunk = response.content
return chunk
def trailer():
resp = requests.head("https://futuredisk-web.chal.uiuc.tf/haystack.bin.gz")
cl = resp.headers['content-length']
offset = int(cl) - 8
response = requests.get(
"https://futuredisk-web.chal.uiuc.tf/haystack.bin.gz",
headers={"Range": f"bytes={offset}-{cl}"}
)
chunk = response.content
return chunk
head = header(0, 24)
binflag = bin(huh, 14)
trail = trailer()
out.write(head)
out.write(binflag)
out.write(trail) import contextlib
@contextlib.contextmanager
def patch_gzip_for_partial():
"""
Context manager that replaces gzip.GzipFile._read_eof with a no-op.
This is useful when decompressing partial files, something that won't
work if GzipFile does it's checksum comparison.
"""
_read_eof = gzip.GzipFile._read_eof
gzip.GzipFile._read_eof = lambda *args, **kwargs: None
yield
gzip.GzipFile._read_eof = _read_eoffrom cStringIO import StringIO
with patch_gzip_for_partial():
decompressed = gzip.GzipFile(StringIO(compressed)).read() example usage in thereimport contextlib
@contextlib.contextmanager
def patch_gzip_for_partial():
"""
Context manager that replaces gzip.GzipFile._read_eof with a no-op.
This is useful when decompressing partial files, something that won't
work if GzipFile does it's checksum comparison.
"""
_read_eof = gzip.GzipFile._read_eof
gzip.GzipFile._read_eof = lambda *args, **kwargs: None
yield
gzip.GzipFile._read_eof = _read_eof bytes=9223372036854774807-9223372036854775807? (edited)import requests
huh = 8214
out = open("final.bin", "wb")
def header(offset, l):
response = requests.get(
"https://futuredisk-web.chal.uiuc.tf/haystack.bin.gz",
headers={"Range": f"bytes={offset}-{offset + l - 1}"}
)
chunk = response.content
return chunk
def bin(offset, l):
response = requests.get(
"https://futuredisk-web.chal.uiuc.tf/haystack.bin.gz",
headers={"Range": f"bytes={offset}-{offset + l - 1}"}
)
chunk = response.content
return chunk
def trailer():
resp = requests.head("https://futuredisk-web.chal.uiuc.tf/haystack.bin.gz")
cl = resp.headers['content-length']
offset = int(cl) - 8
response = requests.get(
"https://futuredisk-web.chal.uiuc.tf/haystack.bin.gz",
headers={"Range": f"bytes={offset}-{cl}"}
)
chunk = response.content
return chunk
head = header(0, 24)
binflag = bin(huh, 14)
trail = trailer()
out.write(head)
out.write(binflag)
out.write(trail)